import { useRef } from 'react';
import { useSyncState } from './useSyncState';
/** 对象操作方法库 */
import _ from 'lodash';


export const useReactive = <T extends Record<string,any>>(params: T): T => {

    /** 缓存state，解决设置setState时因为闭包导致state不是最新值 */
    const cacheReactiveState = useRef<T>(params);



    /** 根据对象属性路径，生成Proxy代理对象的handle */
    const getProxyHandle = <S extends Record<string, any>>(path: Array<string>) => {
        return {
            set(target: S, key: string, value: any) {
                Object.assign(target, { [key]: value });
                /** 获取缓存state并修改属性值 */
                const cloneState = _.set({ ...cacheReactiveState.current }, [...path, key], value);
                /** 因为cloneState已经被拓展运算符结构了，需要重新构建响应式Proxy对象 */
                const _state = createReactiveObject(cloneState)
                setState(_state, () => {
                    /** 更新缓存state */
                    cacheReactiveState.current = _state
                })
                return true
            },
            get(target: S, key: string) {
                if (typeof target[key] === 'function') {
                    /** 克隆缓存state原理同上注释 */
                    return (...args: Array<any>) => {
                        const result = target[key]?.(...args);
                        const cloneState = _.set({ ...cacheReactiveState.current }, [...path], target)
                        const res = createReactiveObject(cloneState);
                        setState(res, () => {
                            cacheReactiveState.current = res
                        })
                        return result;
                    }
                }
                return target[key]
            }
        }
    }


    /** 根据对象路径递归生成Proxy代理对象，实现对象每一层都是个Proxy响应式对象 */
    const createReactiveObject = <U extends Record<string, any>>(_params: U, path: Array<string> = []): U => {
        // 不是对象直接导出
        if (typeof _params !== 'object') return _params;
        // 遍历对象深层属性，对于引用型属性递归生成proxy对象
        Object.keys(_params).forEach((key: string) => {
            if (typeof _params[key] === 'object') {
                const targetReactive = _params[key] instanceof Array ? [..._params[key]] : { ..._params[key] };
                Object.assign(_params, {
                    [key]: createReactiveObject(targetReactive, [...path, key])
                })
            }
        })
        const resReactive: any = _params instanceof Array ? [..._params] : { ..._params };
        return new Proxy<U>(resReactive, getProxyHandle([...path]))
    }

    const reactiveObject = createReactiveObject<T>(params);
    cacheReactiveState.current = reactiveObject;
    const [state, setState] = useSyncState(reactiveObject);
    return state;
}